package com.moon.aop;

import com.moon.anno.ReCommit;
import com.moon.utils.JsonUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@Aspect
public class ReCommitAop {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;

    @Pointcut("execution(* com.moon.controller.*.*(..))")
    public void point() {
        //
    }

    @Around("point()")
    public Object around(ProceedingJoinPoint p) throws Throwable {
        //判断该方法是否需要防重提
        MethodSignature methodSignature = (MethodSignature) p.getSignature();
        Method method = methodSignature.getMethod();
        ReCommit reCommit = method.getDeclaredAnnotation(ReCommit.class);
        if (reCommit != null) {
            //初始化redisson客户端
            Config cfg = new Config();
            cfg.useSingleServer().setAddress("redis://" + host + ":" + port);
            RedissonClient client = Redisson.create(cfg);
            //获得业务key
            Object[] args = p.getArgs();
            List<Object> arglist = new ArrayList();
            for (Object obj : args) {
                if (obj instanceof HttpServletRequest || obj instanceof HttpServletResponse) {
                    continue;
                }
                arglist.add(obj);
            }
            String key = p.getSignature().toShortString() + ":" + JsonUtils.toJson(arglist);
            //防重复提交
            RLock rLock = client.getLock(key);
            boolean flag = rLock.tryLock(0, reCommit.timeout(), TimeUnit.MILLISECONDS);
            if (!flag) {
                return "fail";
            }
        }
        Object r = p.proceed();
        return r;
    }

    //
}
